/*
interface

接口是一个或多个方法签名的集合
只要某个类型拥有该接口的所有方法签名，即算实现该接口，无需显示
	声明实现了哪个接口，这称为structural typing
接口只有方法声明，没有实现，没有数据字段
接口可以匿名嵌入其它接口，或嵌入到机构中
将对象赋值给接口时，会发生拷贝，而接口内部存储的是指向这个
	复制品的指针，即无法对象的状态，也无法获取指针
只有当接口存储的类型和对象都为nil，接口才等于nil
接口等于不会做receiver自动转换
接口同样支持匿名字段方法
接口也可实现类似OOP中的多态
空接口可以作为任何类型数据的容器
*/
package main

import (
	"fmt"
)

//空接口是所有类型的实现的接口，类似java中的Object
type EMPTY interface {
}

type USB interface {
	Name() string
	Connector //嵌入Connector接口
}
type Connector interface {
	Connect()
}

type PhoneConnector struct {
	name string
}

func (pc PhoneConnector) Name() string {
	return pc.name
}

func (pc PhoneConnector) Connect() {
	fmt.Println("Connect:", pc.name)
}

func Disconnect1(usb USB) {
	//通过structVar,ok:=interface.(struct)做接口的类型的判断
	if pc, ok := usb.(PhoneConnector); ok {
		fmt.Println("pc.name =", pc.name)
		return
	}
	fmt.Println("Unknow device.")

}

//匿名空接口
func Disconnect2(usb interface{}) {
	//通过v:=interface.(type)让系统自动判断interface的类型
	switch v := usb.(type) {
	case PhoneConnector:
		fmt.Println("pc.name =", v.name)
	default:
		fmt.Println("Unknow device.")
	}
}

type TvConnector struct {
	name string
}

func (tv *TvConnector) Connect() {
	fmt.Println("Connect:", tv.name)
}

func main() {
	pc := PhoneConnector{"PhoneConnector"}
	var a USB = pc
	a.Connect()
	Disconnect1(a)
	Disconnect2(a)

	var b Connector
	b = Connector(pc)
	b.Connect()
	/*
		接口转换为其嵌入接口,值拷贝：修改对象的属性，
		用之前的对象转换的接口调用，仍然打印之前对象的属性值
	*/
	pc.name = "pc"
	b.Connect()
	/*
		接口转换为其嵌入接口,值拷贝：将修改后的对象转换的接口调用，
		则打印修改后的对象的属性值
	*/
	b = Connector(pc)
	b.Connect()

	c := TvConnector{name: "TvConnTector"}
	b = Connector(&c)
	b.Connect()

	/*
		嵌入接口不可以转换为其接口，否则报如下类似错误
		cannot use c (type TvConnector) as type USB in assignment:
		TvConnector does not implement USB (Connect method has pointer receiver)
	*/
	//	a = c
	fmt.Println("------------------interface nil------------------")
	var e1 interface{}
	fmt.Println(e1)
	fmt.Println(e1 == nil)
	var e2 *int = nil
	fmt.Println(e2)
	fmt.Println(e1 == e2)
}
